﻿/*
  DISCLAIMER:
THIS CODE IS SAMPLE CODE. THESE SAMPLES ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
MICROSOFT FURTHER DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR OF FITNESS FOR
A PARTICULAR PURPOSE. THE ENTIRE RISK ARISING OUT OF THE USE OR PERFORMANCE OF THE SAMPLES REMAINS WITH YOU. IN NO EVENT SHALL
MICROSOFT OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE
SAMPLES, EVEN IF MICROSOFT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION
OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU.
 * */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Exchange.WebServices.Data;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;
using System.DirectoryServices;


using System.ServiceModel;


namespace EWSPushTest
{
    public partial class MainForm : Form
    {
        ExchangeService _esb;
        Logging.ClassLogger _logger;
        PushSubscription _subscription;
        string _watermark = "";

        public MainForm()
        {
            InitializeComponent();
            _logger = new Logging.ClassLogger(false, "");
            _logger.LogListBox = listBoxLog;
            _logger.LogListBoxAutoScroll = true;
            InitListener();
            checkBoxUseDefaultCred.Checked = true;
        }

        private void InitListener()
        {
            // Initialise the listener and receive the events from it
            EWSPushListener.Listener CurrentNotification = new EWSPushListener.Listener();

            CurrentNotification.EWSPushEvent += new EventHandler(CurrentNotification_EventName);

            ServiceHost MyHost = new ServiceHost(CurrentNotification, ListenUri());

            MyHost.Open();

            _logger.Log("Listener service started");

            // Set up timer
            // Check subscription every five and a half minutes (if we hit this, then we know the subscription has failed)
            timer1.Interval = 330000; 
        }

        private Uri ListenUri()
        {
            // Work out the listener address - do this using local machine host name
            string sHostName = Dns.GetHostName();
            return new Uri("http://" + sHostName + ":9008/Notification");
        }


        void CurrentNotification_EventName(object sender, EventArgs e)
        {
            // We have received a push event

            // Reset the subscription check timer
            timer1.Stop();

            EWSPushListener.EWSPushEvent ee = (EWSPushListener.EWSPushEvent)e;
            string eventDetails = "";
            string itemId = "";
            _watermark=ee.Watermark; // Keep track of the watermark in case we lose the subscription

            if (ee.NotificationEvent is EWSPushListener.PushFolderEvent)
            {
                // This is a folder event
                eventDetails = "Folder ";
                try
                {
                    Folder oFolder = Folder.Bind(_esb, (ee.NotificationEvent as EWSPushListener.PushFolderEvent).FolderId);
                    itemId = oFolder.DisplayName;
                }
                catch
                {
                    itemId="Couldn't bind to folder";
                }
                
            }
            else if (ee.NotificationEvent is EWSPushListener.PushItemEvent)
            {
                // This is an item event
                eventDetails = "Item ";
                try
                {
                    Item oItem = Item.Bind(_esb, (ee.NotificationEvent as EWSPushListener.PushItemEvent).ItemId);
                    itemId = oItem.Subject;
                }
                catch
                {
                    itemId="Couldn't bind to item";
                }
            }

            if (ee.EventName == "Status")
            {
                textBoxStatus.Text = DateTime.Now.ToLongTimeString();
            }
            else
                _logger.Log(eventDetails + ee.EventName + ": " + itemId);
            timer1.Start();
        }

        private bool Autodiscover(ExchangeService esb, string username = "")
        {
            string sSMTPAddress = "";
            try
            {
                // Get autodiscover URL, either for current user or specified user
                if (username == "") username = Environment.UserName;

                // Obtain SMTP address for the user
                DirectorySearcher oSearcher = new DirectorySearcher();
                oSearcher.Filter = "(&(objectClass=user)(sAMAccountName=" + username + "))";
                oSearcher.PropertiesToLoad.Add("mail");
                SearchResult oResult = oSearcher.FindOne();
                if (oResult == null)
                {
                    // Failed search, have we been given an email address?
                    oSearcher.Filter = "(&(objectClass=user)(mail=" + username + "))";
                    oResult = oSearcher.FindOne();
                }
                ResultPropertyValueCollection oProperty;
                oProperty = oResult.Properties["mail"];
                sSMTPAddress = oProperty[0].ToString();
            }
            catch (Exception e)
            {
                _logger.Log("Autodiscover failed - unable to retrieve account: " + e.Message);
                return false;
            }
            _logger.Log("Autodiscovering for " + sSMTPAddress);
            esb.AutodiscoverUrl(sSMTPAddress);
            return true;
        }


        private void button1_Click(object sender, EventArgs e)
        {
            if (!(_subscription == null)) return;

            // Ignore SSL errors
            System.Net.ServicePointManager.ServerCertificateValidationCallback =
            delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
            {
                return true;
            };

            _esb = new ExchangeService();

            if (!checkBoxUseDefaultCred.Checked)
            {
                _esb.Credentials = new NetworkCredential(txtUserName.Text, txtPwd.Text);
                _esb.UseDefaultCredentials = false;
            }
            else
                _esb.UseDefaultCredentials = true;

            if (!Autodiscover(_esb)) return;

            // Create a new subscription.
            try
            {
                _subscription = _esb.SubscribeToPushNotificationsOnAllFolders(ListenUri(), 1, "", EventType.Created, EventType.Deleted, EventType.Modified, EventType.Copied, EventType.Moved);
                _logger.Log("Subscription Succeeded: " + _subscription.Id);
                timer1.Start();
            }
            catch (Exception ex)
            {
                _logger.Log("Failed to subscribe: " + ex.Message);
            }

        }

        private void checkBoxUseDefaultCred_CheckedChanged(object sender, EventArgs e)
        {
            txtUserName.Enabled = !checkBoxUseDefaultCred.Checked;
            labelUsername.Enabled = !checkBoxUseDefaultCred.Checked;
            txtPwd.Enabled = !checkBoxUseDefaultCred.Checked;
            labelPassword.Enabled = !checkBoxUseDefaultCred.Checked;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Stop();
            if (_subscription == null) return;

            // The subscription has failed, or we wouldn't be here
            // A status message is sent out every minute.  If we have received nothing in the last five minutes
            // then the subscription needs resetting, as it will have timed out on the server

            if (timer1.Interval!=60000) _logger.Log("Subscription has failed");

            bool bSuccess = false;
            try
            {
                _subscription = _esb.SubscribeToPushNotificationsOnAllFolders(ListenUri(), 1, _watermark, EventType.Created, EventType.Deleted, EventType.Modified, EventType.Copied, EventType.Moved);
                bSuccess = true;
            }
            catch (Exception ex)
            {
                _logger.Log("Failed to resubscribe: " + ex.Message);
            }
            if (bSuccess)
            {
                timer1.Interval = 330000; 
                _logger.Log("Resubscribed with id: " + _subscription.Id);
            }
            else
            {
                timer1.Interval = 60000; // Set interval to one minute if we failed to subscribe (we'll try again then)
            }
            timer1.Start();
        }
    }
}